package com.ruicar.afs.cloud.risk.tortoise.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruicar.afs.cloud.common.core.enums.AfsEnumUtil;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.common.modules.afscorebusiness.enums.WhetherEnum;
import com.ruicar.afs.cloud.risk.api.dto.tortoise.TortoiseResultNotice;
import com.ruicar.afs.cloud.risk.api.dto.tortoise.notice.*;
import com.ruicar.afs.cloud.risk.api.dto.tortoise.TortoiseQueryBean;
import com.ruicar.afs.cloud.risk.api.enums.tortoise.GwtTortoiseExceptPledge;
import com.ruicar.afs.cloud.risk.api.enums.tortoise.GwtTortoiseFraudResult;
import com.ruicar.afs.cloud.risk.api.enums.tortoise.GwtTortoisePrevResult;
import com.ruicar.afs.cloud.risk.tortoise.components.GwtInnerServerRequest;
import com.ruicar.afs.cloud.risk.tortoise.consts.GwtTortoiseConst;
import com.ruicar.afs.cloud.risk.tortoise.dto.notice.GwtTortoiseReq;
import com.ruicar.afs.cloud.risk.tortoise.dto.notice.data.*;
import com.ruicar.afs.cloud.risk.tortoise.entity.CreditTortoiseRecord;
import com.ruicar.afs.cloud.risk.tortoise.enums.GwtTortoiseNoticeClientUri;
import com.ruicar.afs.cloud.risk.tortoise.enums.GwtTortoiseQueryStatus;
import com.ruicar.afs.cloud.risk.tortoise.mapper.CreditQueryRecordMapper;
import com.ruicar.afs.cloud.risk.tortoise.service.AfsCreditTortoiseRecordService;
import com.ruicar.afs.cloud.risk.tortoise.util.AfsEnumEmptyUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

/**
 * @Description
 * @Author Peng Jinbo
 * @Date 2020/07/30 21:45
 */
@Service
@AllArgsConstructor
public class AfsCreditTortoiseRecordServiceImpl extends ServiceImpl<CreditQueryRecordMapper, CreditTortoiseRecord> implements AfsCreditTortoiseRecordService {

    /**
     * Gwt client notice request
     */
    private final GwtInnerServerRequest gwtClientNoticeRequest;

    /**
     * Author Peng Jinbo
     * Save record *
     *
     * @param queryBean       query bean
     * @param tortoiseTrackId tortoise track id
     */
    @Override
    public void saveRecord(TortoiseQueryBean queryBean,String tortoiseTrackId) {
        CreditTortoiseRecord record = CreditTortoiseRecord.builder()
                .applyNo(queryBean.getApplyNo())
                .serialNumber(queryBean.getSerialNumber())
                .mainId(queryBean.getMainId())
                .sceneCode(queryBean.getScene())
                .callStep(queryBean.getCallStep())
                .clientServerName(queryBean.getServerName())
                .queryDate(new Date())
                .recordStatus(GwtTortoiseQueryStatus.WAIT)
                .trackId(tortoiseTrackId)
                .build();
        this.save(record);
    }

    /**
     * @return the notice
     * @Author Peng Jinbo Gets notice *
     */
    @Override
    public TortoiseResultNotice getNotice(String serialNumber) {
        CreditTortoiseRecord record = queryRecord(serialNumber);
        if(record.getRecordStatus() == GwtTortoiseQueryStatus.ERR){
            return baseNotice(record,null);
        }else {
            switch (record.getSceneCode()) {
                case NORMAL_APPLY:
                    return applyNotice(record);
                case HAND_DECISION_INVOKE:
                    return handApprove(record);
                case HAND_ANTI_FRAUD_INVOKE:
                    return handFraud(record);
                case FRAUD_PUSH:
                    return fraudPush(record);
                case PREV_APPROVE:
                    return prevApprove(record);
                default:
                    throw new AfsBaseException("未识别的场景类型，通知接收失败 ：{}", AfsEnumUtil.key(record.getSceneCode()));
            }
        }
    }

    @Override
    public CreditTortoiseRecord queryRecord(String serialNumber) {
        return this.getOne(Wrappers.<CreditTortoiseRecord>lambdaQuery()
                .eq(CreditTortoiseRecord::getSerialNumber, serialNumber));
    }

    @Override
    public void noticeToClient(GwtTortoiseReq notice, GwtTortoiseNoticeClientUri noticeUri) {
        CreditTortoiseRecord record = this.queryRecord(notice.getSysCallLogId());
        if (Objects.isNull(record)) {
            throw new AfsBaseException("不存在操作记录，通知数据接收失败. ");
        }
        if(GwtTortoiseConst.NOTICE_CODE_SUCCESS.equals(notice.getErrorCode())) {
            record.setNoticeResult(Objects.isNull(notice.getData()) ? "" : notice.getData().toJSONString());
            record.setRecordStatus(GwtTortoiseQueryStatus.DONE);
        }else{
            record.setNoticeResult(notice.getErrorMsg());
            record.setRecordStatus(GwtTortoiseQueryStatus.ERR);

        }
        record.setNoticeDate(new Date());
        this.updateById(record);
        TortoiseResultNotice resultNotice = this.getNotice(notice.getSysCallLogId());
        gwtClientNoticeRequest.post(record.getClientServerName(), AfsEnumUtil.key(noticeUri), resultNotice);
    }


    /**
     * Author Peng Jinbo
     * Prev approve tortoise result notice
     *
     * @param record record
     * @return the tortoise result notice
     */
    private TortoiseResultNotice prevApprove(CreditTortoiseRecord record) {
        Assert.isTrue(StrUtil.isNotBlank(record.getNoticeResult()),"暂未获取到玄武返回");
        GwtTortoisePrevApproveNotice gwtTortoisePrevApproveNotice = JSONObject.parseObject(record.getNoticeResult(), GwtTortoisePrevApproveNotice.class);
        String resultStr = AfsEnumEmptyUtil.key(gwtTortoisePrevApproveNotice.getPrevCreditResult());
        if(GwtTortoisePrevResult.AGREE == gwtTortoisePrevApproveNotice.getPrevCreditResult()){
            resultStr = WhetherEnum.YES.getCode();
        }else if(GwtTortoisePrevResult.REFUSE == gwtTortoisePrevApproveNotice.getPrevCreditResult()){
            resultStr = WhetherEnum.NO.getCode();
        }
        return baseNotice(record, TortoisePrevApproveNoticeInfo.builder()
                .type(gwtTortoisePrevApproveNotice.getFlowType())
                .result(resultStr)
                .priceLimit(Convert.toBigDecimal(gwtTortoisePrevApproveNotice.getPriceLimit(), BigDecimal.ZERO))
                .refuseReason(gwtTortoisePrevApproveNotice.getRefuseReason())
                .prevApproveDate(gwtTortoisePrevApproveNotice.getPrevCreditDate())
                .build());

    }

    /**
     * Author Peng Jinbo
     * Fraud push tortoise result notice
     *
     * @param record record
     * @return the tortoise result notice
     */
    private TortoiseResultNotice fraudPush(CreditTortoiseRecord record) {
        Assert.isTrue(StrUtil.isNotBlank(record.getNoticeResult()),"暂未获取到玄武返回");
        GwtTortoiseFraudPushNotice gwtTortoiseHandDecisionNotice = JSONObject.parseObject(record.getNoticeResult(), GwtTortoiseFraudPushNotice.class);
        TortoiseFraudPushNoticeInfo fraudPushNotice = TortoiseFraudPushNoticeInfo.builder()
                .result(AfsEnumEmptyUtil.desc(gwtTortoiseHandDecisionNotice.getAntiFraudPushResult()))
                .build();
        return baseNotice(record, fraudPushNotice);
    }

    /**
     * Author Peng Jinbo
     * Hand fraud tortoise result notice
     *
     * @param record record
     * @return the tortoise result notice
     */
    private TortoiseResultNotice handFraud(CreditTortoiseRecord record) {
        Assert.isTrue(StrUtil.isNotBlank(record.getNoticeResult()),"暂未获取到玄武返回");
        GwtTortoiseHandFraudNotice gwtTortoiseHandDecisionNotice = JSONObject.parseObject(record.getNoticeResult(), GwtTortoiseHandFraudNotice.class);
        TortoiseHandFraudNoticeInfo handFraudNotice = TortoiseHandFraudNoticeInfo.builder()
                .result(gwtTortoiseHandDecisionNotice.getFraudResultDesc())
                .score(gwtTortoiseHandDecisionNotice.getFraudScore())
                .build();
        return baseNotice(record, handFraudNotice);
    }

    /**
     * Author Peng Jinbo
     * Hand approve tortoise result notice
     *
     * @param record record
     * @return the tortoise result notice
     */
    private TortoiseResultNotice handApprove(CreditTortoiseRecord record) {
        Assert.isTrue(StrUtil.isNotBlank(record.getNoticeResult()),"暂未获取到玄武返回");
        GwtTortoiseHandDecisionNotice gwtTortoiseHandDecisionNotice = JSONObject.parseObject(record.getNoticeResult(), GwtTortoiseHandDecisionNotice.class);
        TortoiseHandDecisionNoticeInfo decisionNotice = TortoiseHandDecisionNoticeInfo.builder()
                .result(AfsEnumEmptyUtil.key(gwtTortoiseHandDecisionNotice.getDecisionEngineResult()))
                .pledgeFlag((GwtTortoiseExceptPledge.YES == gwtTortoiseHandDecisionNotice.getDecisionEnginePledgeFlag() ? WhetherEnum.YES.getCode() : WhetherEnum.NO.getCode()))
                .exposureMoney(Convert.toBigDecimal(gwtTortoiseHandDecisionNotice.getExposureMoney(), BigDecimal.ZERO))
                .loanMoneyHistory(Convert.toBigDecimal(gwtTortoiseHandDecisionNotice.getLoanMoneyHistory(), BigDecimal.ZERO))
                .score(gwtTortoiseHandDecisionNotice.getDecisionEngineScore())
                .scoreLevel(gwtTortoiseHandDecisionNotice.getDecisionEngineScoreDisplay())
                .build();
        return baseNotice(record, decisionNotice);
    }

    /**
     * Author Peng Jinbo
     * Apply notice tortoise result notice
     *
     * @param record record
     * @return the tortoise result notice
     */
    private TortoiseResultNotice applyNotice(CreditTortoiseRecord record) {
        Assert.isTrue(StrUtil.isNotBlank(record.getNoticeResult()),"暂未获取到玄武返回");
        GwtTortoiseApplyNotice applyNotice = JSONObject.parseObject(record.getNoticeResult(), GwtTortoiseApplyNotice.class);
        List tagList = new ArrayList<>();
        if (StrUtil.isNotBlank(applyNotice.getTagData())) {
            String[] tags = applyNotice.getTagData().split(GwtTortoiseConst.TAGS_STR_SEPARATOR);
            Arrays.stream(tags).filter(StrUtil::isNotBlank).forEach(tag -> tagList.add(tag));
        }

        GwtTortoiseCreditScoreData creditScoreData = Objects.nonNull(applyNotice.getCreditScore()) ? applyNotice.getCreditScore() : GwtTortoiseCreditScoreData.builder().build();
        TortoiseApplyNoticeInfo noticeInfo = TortoiseApplyNoticeInfo.builder()
                .result(Objects.nonNull(applyNotice.getDecisionEngineResult()) ? AfsEnumUtil.key(applyNotice.getDecisionEngineResult()) : "")
                .pledgeFlag(StrUtil.equals(AfsEnumEmptyUtil.key(applyNotice.getDecisionEnginePledgeFlag()), AfsEnumEmptyUtil.key(GwtTortoiseExceptPledge.YES))
                        ? WhetherEnum.YES.getCode() : WhetherEnum.NO.getCode())
                .score(applyNotice.getDecisionEngineScore())
                .scoreLevel(applyNotice.getDecisionEngineScoreDisplay())
                .antiFraudResult(AfsEnumEmptyUtil.key(applyNotice.getAntiFraudResult()))
                .antiFraudScore(applyNotice.getAntiFraudScore())
                .customerScore(creditScoreData.getCustomerScore())
                .bondScore(creditScoreData.getBondScore())
                .sponsorScore(creditScoreData.getSponsorScore())
                .tagList(tagList)
                .autoReject(applyNotice.getAntiFraudResult() == GwtTortoiseFraudResult.HIGH_RISK && StrUtil.equals(applyNotice.getCallStep(),"10"))
                .build();

        return baseNotice(record, noticeInfo);
    }

    /**
     * Author Peng Jinbo
     * Base notice tortoise result notice
     *
     * @param record     record
     * @param noticeInfo notice info
     * @return the tortoise result notice
     */
    private TortoiseResultNotice baseNotice(CreditTortoiseRecord record, Object noticeInfo) {
        return TortoiseResultNotice.builder()
                .mainId(record.getMainId())
                .noticeInfo(noticeInfo)
                .applyNo(record.getApplyNo())
                .callStep(record.getCallStep())
                .scene(record.getSceneCode())
                .serialNumber(record.getSerialNumber())
                .noticeDate(record.getNoticeDate())
                .err(record.getRecordStatus() == GwtTortoiseQueryStatus.ERR)
                .errMsg(record.getRecordStatus() == GwtTortoiseQueryStatus.ERR ? record.getNoticeResult() : "")
                .build();
    }


}
